﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
using System.Web;

namespace Protocal.App.Common
{
    public static class HttpHelper
    {
        private static HttpClient _httpClient;
        private static WebProxy _proxy;
        private static bool _useProxy = false;
        private static readonly object _lock = new object();

        static HttpHelper()
        {
            _httpClient = CreateHttpClient();
        }

        /// <summary>
        /// Set proxy for all subsequent requests
        /// </summary>
        /// <param name="proxyAddress">Proxy address (e.g., "http://proxy.example.com:8080")</param>
        /// <param name="credentials">Optional credentials</param>
        public static void SetProxy(string proxyAddress, ICredentials credentials = null)
        {
            if (string.IsNullOrWhiteSpace(proxyAddress))
            {
                throw new Exception("代理地址不能为空");
            }

            lock (_lock)
            {
                _proxy = new WebProxy(new Uri(proxyAddress));
                if (credentials != null)
                {
                    _proxy.Credentials = credentials;
                }

                _useProxy = true;
                UpdateHttpClient();
            }
        }

        /// <summary>
        /// Clear proxy settings
        /// </summary>
        public static void ClearProxy()
        {
            lock (_lock)
            {
                _proxy = null;
                _useProxy = false;
                UpdateHttpClient();
            }
        }

        private static void UpdateHttpClient()
        {
            lock (_lock)
            {
                // Create a new HttpClient with updated settings
                var oldClient = _httpClient;
                _httpClient = CreateHttpClient();

                // Schedule the old client for disposal
                Task.Run(async () => {
                    await Task.Delay(TimeSpan.FromSeconds(10)); // Wait for pending requests
                    oldClient?.Dispose();
                });
            }
        }

        private static HttpClient CreateHttpClient()
        {
            var handler = new HttpClientHandler
            {
                UseProxy = _useProxy,
                Proxy = _proxy,
                // Recommended settings for better performance and security
                UseCookies = false,
                AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate
            };

            var client = new HttpClient(handler)
            {
                Timeout = TimeSpan.FromSeconds(30)
            };

            // Set default headers if needed
            client.DefaultRequestHeaders.Add("User-Agent", "HttpHelper/1.0");
            client.DefaultRequestHeaders.Add("Accept", "application/json");

            return client;
        }

        #region GET Methods

        public static async Task<string> GetAsync(string url, Dictionary<string, string> headers = null)
        {
            return await GetAsync(url, null, headers);
        }

        public static async Task<string> GetAsync(string url, Dictionary<string, string> parameters, Dictionary<string, string> headers = null)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentException("URL cannot be null or empty", nameof(url));
            }

            var requestUrl = BuildUrlWithQueryString(url, parameters);

            using (var request = new HttpRequestMessage(HttpMethod.Get, requestUrl))
            {
                AddHeaders(request, headers);
                return await SendRequestAsync(request);
            }
        }

        #endregion

        #region POST Methods

        public static async Task<string> PostFormUrlEncodedAsync(string url, Dictionary<string, string> formData, Dictionary<string, string> headers = null)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentException("URL cannot be null or empty", nameof(url));
            }

            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                AddHeaders(request, headers);

                if (formData != null)
                {
                    request.Content = new FormUrlEncodedContent(formData);
                }

                return await SendRequestAsync(request);
            }
        }

        public static async Task<string> PostJsonAsync(string url, string jsonContent, Dictionary<string, string> headers = null)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentException("URL cannot be null or empty", nameof(url));
            }

            using (var request = new HttpRequestMessage(HttpMethod.Post, url))
            {
                AddHeaders(request, headers);

                if (!string.IsNullOrEmpty(jsonContent))
                {
                    request.Content = new StringContent(jsonContent, Encoding.UTF8, "application/json");
                }

                return await SendRequestAsync(request);
            }
        }

        public static async Task<string> PostMultipartFormDataAsync(
            string url,
            Dictionary<string, string> formData,
            Dictionary<string, Tuple<string, byte[]>> fileData = null,
            Dictionary<string, string> headers = null)
        {
            if (string.IsNullOrWhiteSpace(url))
            {
                throw new ArgumentException("URL cannot be null or empty", nameof(url));
            }

            var request = new HttpRequestMessage(HttpMethod.Post, url);
            var multipartContent = new MultipartFormDataContent();

            try
            {
                AddHeaders(request, headers);

                if (formData != null)
                {
                    foreach (var item in formData)
                    {
                        multipartContent.Add(new StringContent(item.Value), item.Key);
                    }
                }

                if (fileData != null)
                {
                    foreach (var file in fileData)
                    {
                        var fileContent = new ByteArrayContent(file.Value.Item2);
                        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue("application/octet-stream");
                        multipartContent.Add(fileContent, file.Key, file.Value.Item1);
                    }
                }

                request.Content = multipartContent;
                return await SendRequestAsync(request);
            }
            catch
            {
                multipartContent?.Dispose();
                request?.Dispose();
                throw;
            }
        }

        #endregion

        #region Helper Methods

        private static string BuildUrlWithQueryString(string url, Dictionary<string, string> parameters)
        {
            if (parameters == null || parameters.Count == 0)
            {
                return url;
            }

            var uriBuilder = new UriBuilder(url);
            var query = HttpUtility.ParseQueryString(uriBuilder.Query);

            foreach (var param in parameters)
            {
                query[param.Key] = param.Value;
            }

            uriBuilder.Query = query.ToString();
            return uriBuilder.ToString();
        }

        private static void AddHeaders(HttpRequestMessage request, Dictionary<string, string> headers)
        {
            if (headers == null) return;

            foreach (var header in headers)
            {
                if (header.Key.Equals("Content-Type", StringComparison.OrdinalIgnoreCase) ||
                    header.Key.Equals("Content-Length", StringComparison.OrdinalIgnoreCase))
                {
                    continue;
                }

                request.Headers.TryAddWithoutValidation(header.Key, header.Value);
            }
        }

        private static async Task<string> SendRequestAsync(HttpRequestMessage request)
        {
            try
            {
                using (var response = await _httpClient.SendAsync(request))
                {
                    response.EnsureSuccessStatusCode();
                    return await response.Content.ReadAsStringAsync();
                }
            }
            catch (HttpRequestException ex)
            {
                throw new Exception($"HTTP request failed: {ex.Message}", ex);
            }
            catch (TaskCanceledException ex)
            {
                throw new Exception("Request timed out", ex);
            }
            finally
            {
                request?.Dispose();
            }
        }

        #endregion
    }
}
